using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Serialization;
using Framework.DataObjects;
using Framework.StringSplit;
using Newtonsoft.Json;

namespace Framework
{
    public static class ExtensionMethodUtil
    {
        // *********************************************************************************
        // String
        // *********************************************************************************

        #region Format
        /// <summary>
        /// Formats the specified target string using the string.Format.
        /// </summary>
        /// <example>
        /// "The quick brown {0}, jumped over the lazy {1}".FormatWith("Fox", "Dog");
        /// Is equivlent to:
        /// string.Format("The quick brown {0}, jumped over the lazy {1}", "Fox", "Dog");
        /// </example>
        /// <param name="target">The target string.</param>
        /// <param name="args">The format arguments.</param>
        /// <returns></returns>
        public static string FormatWith(this string target, params object[] args)
        {
            //Check.Argument.IsNotEmpty(target, "target can not be empty");

            return String.Format(CultureInfo.CurrentCulture, target, args);
        }
        #endregion

        #region HtmlEncode

        /// <summary>
        /// HTMLs the encode.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        /// <remarks>
        /// See: http://weblogs.sqlteam.com/mladenp/archive/2008/10/21/Different-ways-how-to-escape-an-XML-string-in-C.aspx
        /// This is different to System.Web.HttpUtility.HtmlEncode().
        /// HttpUtility.HtmlEncode will not encode an apostrophe, so if you don't want to encode appostrophes then pass in false
        /// </remarks>
        public static string HtmlEncode(this string text)
        {
            return XmlUtil.Escape(text);
        }

        /// <summary>
        /// HTMLs the encode.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="encodeApostrophe">if set to <c>true</c> [encode apostrophe].</param>
        /// <returns></returns>
        /// <remarks>
        /// See: http://weblogs.sqlteam.com/mladenp/archive/2008/10/21/Different-ways-how-to-escape-an-XML-string-in-C.aspx
        /// This is different to System.Web.HttpUtility.HtmlEncode().
        /// HttpUtility.HtmlEncode will not encode an apostrophe, so if you don't want to encode appostrophes then pass in false
        /// </remarks>
        public static string HtmlEncode(this string text, bool encodeApostrophe)
        {
            return XmlUtil.Escape(text, encodeApostrophe);
        }

        #endregion

        #region ToDefaultValue
        public static string ToDefaultValue(this string value)
        {
            return StrUtil.DefaultValue(value);
        }
        public static string ToDefaultValue(this string value, string defaultValue)
        {
            return StrUtil.DefaultValue(value, defaultValue);
        }
        #endregion

        // *********************************************************************************
        // Primitive Types
        // *********************************************************************************

        #region ToNullSafeString
        public static string ToNullSafeString(this object value)
        {
            return value == null ? String.Empty : value.ToString();
        }
        #endregion

        // *********************************************************************************
        // As Simple Types
        // *********************************************************************************

        #region AsBool     

        /// <summary>
        /// Gets as bool.
        /// </summary>
        /// <returns></returns>
        public static bool AsBool(this string value)
        {
            return Is.True(value);
        }

        /// <summary>
        /// Gets as bool.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">if set to <c>true</c> [default value].</param>
        /// <returns></returns>
        public static bool AsBool(this string value, bool defaultValue)
        {
            return Is.EmptyString(value) ? defaultValue : Is.True(value);
        }

        /// <summary>
        /// Gets as bool.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static bool AsBool(this int value)
        {
            return value == 1;
        }
        #endregion

        #region AsInt       
        /// <summary>
        /// Gets as int.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static int AsInt(this string value)
        {
            return NumUtil.ParseInt(value);
        }

        /// <summary>
        /// Gets as int.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static int AsInt(this string value, int defaultValue)
        {
            return NumUtil.ParseInt(value, defaultValue);
        }

        /// <summary>
        /// Gets as int.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static int AsInt(this bool value)
        {
            return value ? 1 : 0;
        }
        #endregion

        #region AsNullableInt
        /// <summary>
        /// Get string as int or null if invalid.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static int? AsNullableInt(this string value)
        {
            return NumUtil.ParseNullableInt(value);
        }
        #endregion

        #region AsLong       

        /// <summary>
        /// Gets as long.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static long AsLong(this string value)
        {
            return NumUtil.ParseLong(value);
        }

        /// <summary>
        /// Gets as long.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static long AsLong(this string value, long defaultValue)
        {
            return NumUtil.ParseLong(value, defaultValue);
        }

        #endregion

        #region AsFloat

        /// <summary>
        /// Gets as float.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static float AsFloat(this string value)
        {
            return NumUtil.ParseFloat(value);
        }

        /// <summary>
        /// Gets as float.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static float AsFloat(this string value, float defaultValue)
        {
            return NumUtil.ParseFloat(value, defaultValue);
        }

        #endregion

        #region AsFloatStripToken

        /// <summary>
        /// Gets as float, strips any numeric tokens found in the string before parsing.  i.e. $,%
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static float AsFloatStripToken(this string value)
        {
            return NumUtil.ParseFloat(NumUtil.StripNumericTokens(value));
        }

        /// <summary>
        /// Gets as Float, strips any numeric tokens found in the string before parsing.  i.e. $,%
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static float AsFloatStripToken(this string value, float defaultValue)
        {
            return NumUtil.ParseFloat(NumUtil.StripNumericTokens(value), defaultValue);
        }

        #endregion

        #region AsDouble

        /// <summary>
        /// Gets as double.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static double AsDouble(this string value)
        {
            return NumUtil.ParseDouble(value);
        }

        /// <summary>
        /// Gets as double.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static double AsDouble(this string value, double defaultValue)
        {
            return NumUtil.ParseDouble(value, defaultValue);
        }

        #endregion

        #region AsDoubleStripToken

        /// <summary>
        /// Gets as double, strips any numeric tokens found in the string before parsing.  i.e. $,%
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static double AsDoubleStripToken(this string value, double defaultValue)
        {
            return NumUtil.ParseDouble(NumUtil.StripNumericTokens(value), defaultValue);
        }

        #endregion

        #region AsDecimal

        /// <summary>
        /// Gets as decimal.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static decimal AsDecimal(this string value)
        {
            return NumUtil.ParseDecimal(value);
        }

        /// <summary>
        /// Gets as decimal.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static decimal AsDecimal(this string value, decimal defaultValue)
        {
            return NumUtil.ParseDecimal(value, defaultValue);
        }

        #endregion

        #region AsDecimalStripToken

        /// <summary>
        /// Gets as decimal, strips any numeric tokens found in the string before parsing.  i.e. $,%
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static decimal AsDecimalStripToken(this string value)
        {
            return NumUtil.ParseDecimal(NumUtil.StripNumericTokens(value));
        }

        /// <summary>
        /// Gets as decimal, strips any numeric tokens found in the string before parsing.  i.e. $,%
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static decimal AsDecimalStripToken(this string value, decimal defaultValue)
        {
            return NumUtil.ParseDecimal(NumUtil.StripNumericTokens(value), defaultValue);
        }

        #endregion

        #region AsEnum

        /// <summary>
        /// Gets as Enum.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static T AsEnum<T>(this string value)
        {
            return EnumUtil.Parse<T>(value);
        }

        /// <summary>
        /// Gets as Enum.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defaultValue">Default value if value is invalid </param>
        /// <returns></returns>
        public static T AsEnum<T>(this string value, T defaultValue)
        {
            return EnumUtil.Parse<T>(value, defaultValue);
        }
        #endregion

        #region AsString
        /// <summary>
        /// Converts a boolean to a string.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="formatBoolType">Format type for the boolean.</param>
        /// <returns></returns>
        public static string AsString(this bool value, FormatBoolType formatBoolType)
        {
            return FormatUtil.ToBool(value, formatBoolType);
        }
        #endregion

        // *********************************************************************************
        // POCO Extensions - General
        // *********************************************************************************

        #region PocoApplyDefaultValues
        /// <summary>
        /// Implements precompiled setters with embedded constant values from DefaultValueAttributes
        /// </summary>
        /// <remarks>
        /// http://www.codeproject.com/KB/dotnet/DefValInit.aspx
        /// </remarks>
        /// <example>
        /// public class AOPInitialized
        /// {
        ///     public AOPInitialized()
        ///     {
        ///         this.ApplyDefaultValues();
        ///     }

        ///     [DefaultValue(3)]
        ///     public int IntProperty { get; set; }
        ///     
        ///     [DefaultValue(3)]
        ///     public long LongProperty { get; set; }
        ///     
        ///     [DefaultValue(true)]
        ///     public bool BoolProptrty { get; set; }
        /// }
        /// </example>
        public static void PocoApplyDefaultValues(this object obj)
        {
            var pocoExtension = new PocoExtension();

            pocoExtension.ApplyDefaultValues(obj);
        }
        #endregion

        #region PocoApplyStringLengthLimits
        /// <summary>
        /// Ensures that any string property that uses <typeparamref name="System.ComponentModel.DataAnnotations.StringLength"/> does not break that limit by trimming the strings in question.
        /// This can be applied to SQL entity type POCO's so that you don't get overflow errors if the data is to long for the field.
        /// </summary>
        /// <remarks>
        /// Implements precompiled property trimming in compiled cache for performance reasons.
        /// 
        /// </remarks>
        /// <example>
        /// public class MyEntity
        /// {
        ///     [System.ComponentModel.DataAnnotations.StringLength(20)]
        ///     public string StringLength20 { get; set; }
        ///     
        ///     [System.ComponentModel.DataAnnotations.StringLength(100)]
        ///     public string StringLength100 { get; set; }
        ///     
        ///     public string StringLengthAnyLength { get; set; }
        ///     
        ///     public string StringLengthNull { get; set; }
        /// }
        /// 
        /// var string1000 = StrUtil.Replicate(1000, "*");
        /// 
        /// var entity = new MyEntity
        /// {
        ///     StringLengthAnyLength = string1000,
        ///     StringLength100 = string1000,
        ///     StringLengthNull = null
        /// };
        /// 
        /// entity.PocoApplyStringLengthLimits();
        /// 
        /// Assert.AreEqual(entity.StringLength100.Length, 100);
        /// </example>
        public static void PocoApplyStringLengthLimits(this object obj)
        {
            var pocoExtension = new PocoExtension();

            pocoExtension.ApplyStringLengthLimits(obj);
        }
        #endregion

        #region DeepClone
        /// <summary>
        /// Deep clone an object that is marked as [Serializable]
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="a">A.</param>
        /// <returns></returns>
        public static T DeepClone<T>(this T a)
        {

            using (var stream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();

                formatter.Serialize(stream, a);

                stream.Position = 0;

                return (T)formatter.Deserialize(stream);
            }
        }
        #endregion

        // *********************************************************************************
        // POCO Extensions - JSON Serialization
        // ***************************************************************

        //#region ToJavascript
        ///// <summary>
        ///// Converts the Current Object to JSON notation.
        ///// </summary>
        ///// <param name="obj">The obj.</param>
        ///// <returns></returns>
        //public static string ToJavascriptHandleBackslahes(this object obj)
        //{
        //    //var serializer = new JsonSerializer();

        //    return JsonConvert.SerializeObject(obj).Replace(@"\\", @"\\\\");
        //}
        //#endregion

        #region ToJavascript
        /// <summary>
        /// Converts the Current Object to JSON notation.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public static string ToJavascript(this object obj)
        {
            //var serializer = new JavaScriptSerializer();
            
            //return serializer.Serialize(obj);
            return JsonConvert.SerializeObject(obj);
        }
        #endregion

        #region FromJavascript

        /// <summary>
        /// Creates a new instance of the desired object type from a JSON formatted strings.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="javascript">The json.</param>
        /// <returns></returns>
        public static T FromJavascript<T>(this string javascript)
        {
            // Maybe we could use Newton Soft in future
            var serializer = new JavaScriptSerializer();

            return serializer.Deserialize<T>(javascript);
        }
        #endregion

        #region ToJSON

        /// <summary>
        /// Converts the Current Object to JSON notation.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public static string ToJSON(this object obj)
        {
            return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings());
        }
        #endregion

        #region FromJSON

        /// <summary>
        /// Creates a new instance of the desired object type from a JSON formatted strings.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="json">The json.</param>
        /// <returns></returns>
        public static T FromJSON<T>(this string json)
        {
            var settings = new JsonSerializerSettings
                {
                    TypeNameHandling = TypeNameHandling.Objects
                };

            return (T)JsonConvert.DeserializeObject(json, typeof(T), settings);
            //using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
            //{
            //    var serializer = new DataContractJsonSerializer(typeof(T));

            //    return (T)serializer.ReadObject(stream);
            //}
        }

        #endregion

        // *********************************************************************************
        // POCO Extensions - XML Serialization
        // *********************************************************************************

        #region AsXml
        /// <summary>
        /// Render out as XML
        /// </summary>
        /// <returns></returns>
        public static string AsXml(this object o)
        {
            string result = null;

            var output = new MemoryStream();
            var serializer = new XmlSerializer(o.GetType());

            serializer.Serialize(output, o);

            result = Encoding.UTF8.GetString(output.ToArray());

            output.Close();

            return result;
        }

        /// <summary>
        /// Render out as XML, provide any types that are required by the XML Serializer for type resolution
        /// </summary>
        /// <param name="o">The object to serialize as XML.</param>
        /// <param name="extraTypes">The extra types.</param>
        /// <returns></returns>
        public static string AsXml(this object o, Type[] extraTypes)
        {
            string result = null;

            var output = new MemoryStream();
            var serializer = new XmlSerializer(o.GetType(), extraTypes);

            serializer.Serialize(output, o);

            result = Encoding.UTF8.GetString(output.ToArray());

            output.Close();

            return result;
        }
        #endregion

        // *********************************************************************************
        // Type Extensions
        // *********************************************************************************

        #region IsA
        /// <summary>
        /// Determines whether the specified type is of type T.
        /// </summary>
        /// <typeparam name="T">Type to check against</typeparam>
        /// <param name="type">The insance to check.</param>
        /// <returns>
        /// <c>true</c> if the specified type is A; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsA<T>(this Type type)
        {
            return typeof(T).IsAssignableFrom(type);
        }
        #endregion

        // *********************************************************************************
        // List Types
        // *********************************************************************************

        #region AsList

        /// <summary>
        /// Gets as list of strings.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static List<string> AsList(this string value)
        {
            return DelimitedUtil.SplitAsList(value, ',');
        }

        /// <summary>
        /// Gets as list of strings.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static List<string> AsList(this string value, char seperator)
        {
            return DelimitedUtil.SplitAsList(value, seperator);
        }

        /// <summary>
        /// Gets as list of strings.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static List<string> AsList(this string value, string seperator)
        {
            return DelimitedUtil.SplitAsList(value, new[] {seperator});
        }

        public static List<string> AsListCrLf(this string value)
        {
            return DelimitedUtil.SplitAsList(value, new[] { FrameworkConstant.CRLF, FrameworkConstant.LF});
        }

        public static List<string> AsListCrLf(this string value, StringSplitOptionsType options)
        {
            return DelimitedUtil.SplitAsList(options, value, new[] { FrameworkConstant.CRLF, FrameworkConstant.LF });
        }
        #endregion

        #region AsListInt

        /// <summary>
        /// Gets as list of ints.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static List<int> AsListInt(this string value)
        {
            return NumUtil.SplitAsIntList(true, value, ",");
        }

        /// <summary>
        /// Gets as list of ints.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static List<int> AsListInt(this string value, char seperator)
        {
            return NumUtil.SplitAsIntList(true, value, seperator.ToString());
        }

        #endregion

        #region AsArray

        /// <summary>
        /// Gets as list of strings.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static string[] AsArray(this string value)
        {
            return DelimitedUtil.Split(value, ',');
        }

        /// <summary>
        /// Gets as list of strings.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static string[] AsArray(this string value, char seperator)
        {
            return DelimitedUtil.Split(value, seperator);
        }

        #endregion

        #region AsArrayInt

        /// <summary>
        /// Gets as array of ints.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static int[] AsArrayInt(this string value)
        {
            return NumUtil.SplitAsIntList(true, value, ",").ToArray();
        }

        /// <summary>
        /// Gets as array of ints.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static int[] AsArrayInt(this string value, char seperator)
        {
            return NumUtil.SplitAsIntList(true, value, seperator.ToString()).ToArray();
        }

        #endregion

        // *********************************************************************************
        // Dictionary Types
        // *********************************************************************************

        #region AsDictionary
        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="keyValues">The key values.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<KeyValuePair<string, string>> keyValues)
        {
            return AsDictionary(keyValues, FrameworkConstant.DEFAULT_COMPARER);
        }
        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="nameValues">The key values.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<KeyValuePair<string, string>> nameValues, IEqualityComparer<string> comparer)
        {
            return nameValues.ToDictionary(keyValue => keyValue.Key, keyValue => keyValue.Value, comparer);
        }

        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="keyValues">The key values.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<KeyValuePair<string, object>> keyValues)
        {
            return AsDictionary(keyValues, FrameworkConstant.DEFAULT_COMPARER);
        }
        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="nameValues">The key values.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<KeyValuePair<string, object>> nameValues, IEqualityComparer<string> comparer)
        {
            return nameValues.ToDictionary(keyValue => keyValue.Key, keyValue => keyValue.Value == null ? string.Empty : keyValue.Value.ToString(), comparer);
        }

        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="keyValues">The key values.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<NameValueDo> keyValues)
        {
            return AsDictionary(keyValues, FrameworkConstant.DEFAULT_COMPARER);
        }
        /// <summary>
        /// Convert a list of key/values into a dictionary.
        /// </summary>
        /// <param name="nameValues">The key values.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this List<NameValueDo> nameValues, IEqualityComparer<string> comparer)
        {
            return nameValues.ToDictionary(keyValue => keyValue.Name, keyValue => keyValue.Value, comparer);
        }

        /// <summary>
        /// Convert a delimited list of strings into a dictionary. Comma is the default delimiter.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this string value)
        {
            return AsDictionary(value, FrameworkConstant.DEFAULT_COMPARER, ',', AsDictionaryFormatType.KeyOnly);
        }

        /// <summary>
        /// Convert a delimited list of strings into a dictionary. Comma is the default delimiter.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this string value, IEqualityComparer<string> comparer)
        {
            return AsDictionary(value, comparer, ',', AsDictionaryFormatType.KeyOnly);
        }

        /// <summary>
        /// Ases the dictionary.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="formatType">Format type of incoming string value.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this string value, AsDictionaryFormatType formatType)
        {
            return AsDictionary(value, FrameworkConstant.DEFAULT_COMPARER, ',', formatType);
        }

        /// <summary>
        /// Convert a delimited list of strings into a dictionary. Comma is the default delimiter.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <param name="formatType">Format type of incoming string value.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this string value, IEqualityComparer<string> comparer, AsDictionaryFormatType formatType)
        {
            return AsDictionary(value, comparer, ',', formatType);
        }

        /// <summary>
        /// Convert a delimited list of strings into a dictionary.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <param name="seperator">The seperator.</param>
        /// <param name="formatType">Format type of incoming string value.</param>
        /// <returns></returns>
        public static Dictionary<string, string> AsDictionary(this string value, IEqualityComparer<string> comparer, char seperator, AsDictionaryFormatType formatType)
        {
            switch (formatType)
            {
                case AsDictionaryFormatType.KeyOnly:
                    {
                        var list = DelimitedUtil.SplitAsList(value, seperator);

                        return ArrayUtil.AsDictionary(list, comparer);
                    }
                case AsDictionaryFormatType.KeyValue:
                    {
                        var keyValues = DelimitedUtil.SplitAsList(value, seperator);

                        if (keyValues.Count % 2 == 1)
                        {
                            throw new Exception("AsDictionary with Format Type of KeyValue - Must provide an even number of elements");
                        }

                        var result = new Dictionary<string, string>(keyValues.Count / 2, comparer);

                        for (var i = 0; i < keyValues.Count; i += 2)
                        {
                            result.Add(keyValues[i], keyValues[i + 1]);
                        }

                        return result;
                    }
                case AsDictionaryFormatType.KeyValueCrlf:
                    {
                        var list = DelimitedUtil.SplitAsList(value, new[] {FrameworkConstant.CRLF});

                        var result = new Dictionary<string, string>(list.Count, comparer);

                        foreach (var keyValue in list)
                        {
                            var kv = keyValue.AsList();

                            if (kv.Count == 1)
                            {
                                result.Add(kv[0], kv[0]);
                            }
                            else if (kv.Count > 1)
                            {
                                result.Add(kv[0], kv[1]);
                            }
                        }

                        return result;
                    }
            }

            return new Dictionary<string, string>();
        }

        #endregion
        // *********************************************************************************
        // HashSet Types
        // *********************************************************************************

        #region AsHashSet
        /// <summary>
        /// Convert a delimited list of strings into a HashSet. Comma is the default delimiter.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static HashSet<string> AsHashSet(this string value)
        {
            return AsHashSet(value, FrameworkConstant.DEFAULT_COMPARER, ',');
        }

        /// <summary>
        /// Convert a delimited list of strings into a HashSet. Comma is the default delimiter.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <returns></returns>
        public static HashSet<string> AsHashSet(this string value, IEqualityComparer<string> comparer)
        {
            return AsHashSet(value, comparer, ',');
        }

        /// <summary>
        /// Convert a delimited list of strings into a HashSet.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="comparer">The comparer.</param>
        /// <param name="seperator">The seperator.</param>
        /// <returns></returns>
        public static HashSet<string> AsHashSet(this string value, IEqualityComparer<string> comparer, char seperator)
        {
            var list = DelimitedUtil.SplitAsList(value, seperator);

            return ArrayUtil.AsHashSet(list, comparer);
        }
        #endregion
    }
}